﻿using System;

namespace Ngs.MetaData
{
    using System.Collections.Generic;
    using Ngs.Data;

    public sealed class MetaCatalog 
    {
        #region Fields

        private List<MetaType> changes;
        private Dictionary<string, MetaType> types;
        private IMetabase metaBase;
        #endregion

        #region Constructors

        internal MetaCatalog()
            : this(null)
        {
        }

        internal MetaCatalog(IMetabase meta)
        {
            this.metaBase = meta;
            types = new Dictionary<string, MetaType>();
        }
        #endregion

        #region Properties

        internal IList<MetaType> Changes
        {
            get
            {
                this.changes = this.changes ?? new List<MetaType>();
                return this.changes;
            }
        }

        public bool HasStore
        {
            get { return this.metaBase != null; }
        }

        public bool IsInitialized
        {
            get;
            private set;
        }

        public IMetabase MetaBase
        {
            get { return this.metaBase; }
            set
            {
                ExceptionFactory.ThrowInvalidOperation(this.IsInitialized, "The catalog has already been initialized.");
                this.metaBase = value;
            }
        }

        #endregion

        /// <summary>
        /// The Initialize method is idempotent, and thus may be called multiple times.
        /// </summary>
        internal void Initialize()
        {
            if (!this.IsInitialized)
            {
                ExceptionFactory.ThrowInvalidOperation(!this.HasStore, "Catalog must have store in order to load items.");

                IEnumerable<MetaType> result = this.metaBase.LoadMetaTypes();
                foreach (MetaType type in result)
                {
                    this.types.Add(type.Name, type);
                }
                this.IsInitialized = true;
            }
        }

        public void RegisterType(MetaType type)
        {
            if (!this.types.ContainsKey(type.Name))
            {
                this.types.Add(type.Name, type);
                this.Changes.Add(type);
            }
        }

        public MetaType GetType(string typeName)
        {
            MetaType type;
            this.types.TryGetValue(typeName, out type);
            return type;
        }

        public IEnumerable<MetaType> ListTypes()
        {
            Initialize();
            return this.types.Values;
        }

    }
}
